package com.wangfugui.mybatisplus.stock;

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;

public class EnhancedStockExtractorJava8 {
    private static String readHtmlFromResource(String resourceName) throws IOException {
        ClassLoader classLoader = EnhancedStockExtractorJava8.class.getClassLoader();
        try (Scanner scanner = new Scanner(classLoader.getResourceAsStream(resourceName), StandardCharsets.UTF_8.name())) {
            scanner.useDelimiter("\\A");
            return scanner.hasNext() ? scanner.next() : "";
        }
    }
    public static void main(String[] args) throws IOException {
        String htmlContent = readHtmlFromResource("stock_data.html");
        
        List<StockInfo> stockList = extractStockData(htmlContent);
        
        // 过滤掉ST股票
        List<StockInfo> filteredStocks = filterNonSTStocks(stockList);
        
        // 数据分析
        Map<String, Integer> reasonStats = new HashMap<>();
        Map<String, Integer> limitUpStats = new HashMap<>();
        
        for (StockInfo stock : filteredStocks) {
//            // 动因统计
//            String mainReason = extractMainReason(stock.getReason());
//            Integer reasonCount = reasonStats.get(mainReason);
//            reasonStats.put(mainReason, reasonCount == null ? 1 : reasonCount + 1);
//
            // 连板天数统计
            String limitUpDays = stock.getLimitUpDays();
            Integer limitCount = limitUpStats.get(limitUpDays);
            limitUpStats.put(limitUpDays, limitCount == null ? 1 : limitCount + 1);
        }
        
        // 输出结果
        printStockAnalysis(filteredStocks, reasonStats, limitUpStats);
    }
    
    public static List<StockInfo> extractStockData(String htmlContent) {
        List<StockInfo> stockList = new ArrayList<>();
        
        try {
            Document doc = Jsoup.parse(htmlContent);
            Elements rows = doc.select("table.w-100p.watch-table tbody tr");
            
            for (Element row : rows) {
                if (row.select("th").size() > 0) continue;
                
                Elements cells = row.select("td");
                if (cells.size() >= 5) {
                    // 提取股票代码和名称
                    String name = cells.get(0).select("div.f-w-b").text().trim();
                    String fullCode = cells.get(0).select("div.f-s-12.c-666.f-w-n").text().trim();
                    String code = fullCode.replaceAll("[^0-9]", "");
                    
                    // 提取动因
                    Element reasonElement = cells.get(4).selectFirst("div.watch-quote-reason");
                    String reason = reasonElement != null ? 
                        reasonElement.attr("title") : cells.get(4).text().trim();
                    
                    // 提取连续涨停数
                    String limitUpDays = cells.get(3).text().trim();
                    
                    stockList.add(new StockInfo(code, name, reason, limitUpDays, fullCode));
                }
            }
            
        } catch (Exception e) {
            System.err.println("解析错误: " + e.getMessage());
        }
        
        return stockList;
    }
    
    private static List<StockInfo> filterNonSTStocks(List<StockInfo> stocks) {
        List<StockInfo> filtered = new ArrayList<>();
        for (StockInfo stock : stocks) {
            if (!stock.getName().contains("ST") && !stock.getName().contains("*ST")) {
                filtered.add(stock);
            }
        }
        return filtered;
    }
    
    private static String extractMainReason(String reason) {
        if (reason == null || reason.isEmpty()) return "其他";


        // 提取主要动因（第一个"|"之前的内容）
        int pipeIndex = reason.indexOf("|");
        if (pipeIndex > 0) {
            return reason.substring(0, pipeIndex).trim();
        }
// 如果包含"+"，取第一个概念
        int plusIndex = reason.indexOf("+");
        if (plusIndex > 0) {
            return reason.substring(0, plusIndex).trim();
        }

        // 直接返回完整动因，不截断
        return reason;
    }
    
    private static void printStockAnalysis(List<StockInfo> stocks, 
                                         Map<String, Integer> reasonStats,
                                         Map<String, Integer> limitUpStats) {
        System.out.println("📈 涨停股票分析报告 (已过滤ST股)");
        System.out.println("=" + repeatString("=", 80));
        System.out.printf("总计: %d 只涨停股票%n%n", stocks.size());
        
//        // 涨停动因分布
//        System.out.println("🏷️  涨停动因分布:");
//        System.out.println("-" + repeatString("-", 80));
//        printStatMap(reasonStats, stocks.size());
//
        // 连板情况
        System.out.println("🔥 连板情况分析:");
        System.out.println("-" + repeatString("-", 80));
        printLimitUpStats(limitUpStats, stocks.size());
        
        // 详细股票列表
        System.out.println("\n📋 详细股票列表:");
        System.out.println("-" + repeatString("-", 200));
        printStockTable(stocks);
        
//        // 热点分析
//        System.out.println("\n💡 市场热点分析:");
//        System.out.println("-" + repeatString("-", 80));
//        printHotSpotAnalysis(reasonStats, stocks);
    }
    
    private static void printStatMap(Map<String, Integer> statMap, int total) {
        List<Map.Entry<String, Integer>> sorted = new ArrayList<>(statMap.entrySet());
        Collections.sort(sorted, new Comparator<Map.Entry<String, Integer>>() {
            @Override
            public int compare(Map.Entry<String, Integer> a, Map.Entry<String, Integer> b) {
                return b.getValue().compareTo(a.getValue());
            }
        });
        
        for (Map.Entry<String, Integer> entry : sorted) {
            double percentage = (entry.getValue() * 100.0) / total;
            String bar = generateProgressBar(percentage, 20);
            System.out.printf("%-30s: %2d 只 %5.1f%% %s%n", 
                entry.getKey(), entry.getValue(), percentage, bar);
        }
    }
    
    private static void printLimitUpStats(Map<String, Integer> limitUpMap, int total) {
        List<Map.Entry<String, Integer>> sorted = new ArrayList<>(limitUpMap.entrySet());
        Collections.sort(sorted, new Comparator<Map.Entry<String, Integer>>() {
            @Override
            public int compare(Map.Entry<String, Integer> a, Map.Entry<String, Integer> b) {
                return Integer.parseInt(b.getKey()) - Integer.parseInt(a.getKey());
            }
        });
        
        int firstDayCount = 0;
        int multiDayCount = 0;
        
        for (Map.Entry<String, Integer> entry : sorted) {
            int days = Integer.parseInt(entry.getKey());
            int count = entry.getValue();
            double percentage = (count * 100.0) / total;
            
            String emoji = getLimitUpEmoji(days);
            
            System.out.printf("%s 连板%s天: %2d 只 %5.1f%%%n",
                emoji, entry.getKey(), count, percentage);
            
            if (days == 1) {
                firstDayCount = count;
            } else {
                multiDayCount += count;
            }
        }
        
        System.out.printf("\n📊 首板率: %.1f%% | 连板率: %.1f%%%n",
            (firstDayCount * 100.0) / total, (multiDayCount * 100.0) / total);
    }
    
    private static void printStockTable(List<StockInfo> stocks) {
        // 按连板天数降序排列
        Collections.sort(stocks, new Comparator<StockInfo>() {
            @Override
            public int compare(StockInfo a, StockInfo b) {
                return Integer.parseInt(b.getLimitUpDays()) - Integer.parseInt(a.getLimitUpDays());
            }
        });
        
//        System.out.printf("%-4s %-8s %-12s %-8s %s%n",
//            "序号", "代码", "名称", "连板",  "动因");
        System.out.println(repeatString("-", 200));
        
        for (int i = 0; i < stocks.size(); i++) {
            StockInfo stock = stocks.get(i);
            String mainReason = extractMainReason(stock.getReason());
            String limitEmoji = getLimitUpEmoji(Integer.parseInt(stock.getLimitUpDays()));

//            String fullReason = stock.getReason();
            // 处理全部原因，去除换行符和多余空格
            String fullReason = removeLineBreaks(stock.getReason());            System.out.printf("%2d.  %-6s %-10s %-2s%-4s %s%n",
                i + 1,
                stock.getFullCode(),
                stock.getName(),
                limitEmoji,
                stock.getLimitUpDays() + "天",
                fullReason
            );
        }
    }
    private static String removeLineBreaks(String text) {
        if (text == null) return "";
        // 替换所有换行符和连续空格为单个空格
        return text.replaceAll("\\r\\n|\\r|\\n", " ").replaceAll("\\s+", " ").trim();
    }
    private static void printHotSpotAnalysis(Map<String, Integer> reasonStats, List<StockInfo> stocks) {
        // 找出热点动因
        List<Map.Entry<String, Integer>> hotReasons = new ArrayList<>(reasonStats.entrySet());
        Collections.sort(hotReasons, new Comparator<Map.Entry<String, Integer>>() {
            @Override
            public int compare(Map.Entry<String, Integer> a, Map.Entry<String, Integer> b) {
                return b.getValue().compareTo(a.getValue());
            }
        });
        
        // 连板强度分析
        int highLimitCount = 0;
        for (StockInfo stock : stocks) {
            if (Integer.parseInt(stock.getLimitUpDays()) >= 3) {
                highLimitCount++;
            }
        }
        
        System.out.println("🔥 当前市场热点:");
        if (!hotReasons.isEmpty() && hotReasons.get(0).getValue() >= 2) {
            System.out.printf("   • 主要动因: %s (%d只股票)%n", 
                hotReasons.get(0).getKey(), hotReasons.get(0).getValue());
        }
        
        if (highLimitCount > 0) {
            System.out.printf("   • 强势连板: %d只股票连续3天以上涨停%n", highLimitCount);
        }
        
        // 动因多样性分析
        if (reasonStats.size() >= 5) {
            System.out.printf("   • 热点分散: 涉及%d个不同动因%n", reasonStats.size());
        } else {
            System.out.printf("   • 热点集中: 主要围绕%d个动因%n", reasonStats.size());
        }
        
        // 显示前3大热点
        if (hotReasons.size() >= 3) {
            System.out.println("   • 热点排行:");
            for (int i = 0; i < Math.min(3, hotReasons.size()); i++) {
                Map.Entry<String, Integer> entry = hotReasons.get(i);
                System.out.printf("      %d. %s (%d只)%n", i + 1, entry.getKey(), entry.getValue());
            }
        }
    }
    
    private static String getLimitUpEmoji(int days) {
        if (days == 1) return "🆕";
        if (days == 2) return "🔥";
        if (days == 3) return "🚀";
        if (days >= 4) return "💎";
        return "📈";
    }
    
    private static String generateProgressBar(double percentage, int length) {
        int filled = (int) (percentage * length / 100);
        StringBuilder bar = new StringBuilder();
        for (int i = 0; i < length; i++) {
            if (i < filled) {
                bar.append("");
            } else {
                bar.append("");
            }
        }
        return bar.toString();
    }
    
    // Java 8 兼容的字符串重复方法
    private static String repeatString(String str, int count) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < count; i++) {
            sb.append(str);
        }
        return sb.toString();
    }
    
    // 股票信息类
    static class StockInfo {
        private String code;
        private String name;
        private String reason;
        private String limitUpDays;
        private String fullCode;
        
        public StockInfo(String code, String name, String reason, String limitUpDays, String fullCode) {
            this.code = code;
            this.name = name;
            this.reason = reason;
            this.limitUpDays = limitUpDays;
            this.fullCode = fullCode;
        }
        
        public String getCode() { return code; }
        public String getName() { return name; }
        public String getReason() { return reason; }
        public String getLimitUpDays() { return limitUpDays; }
        public String getFullCode() { return fullCode; }
    }
}